package org.dynamo.database.reverse.config.internal;
/*******************************************************************************
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Jean Neveux - initial API and implementation
 *******************************************************************************/
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.dynamo.database.reverse.config.internal.log.Log;
import org.dynamo.database.reverse.config.reader.internal.ConfigLoader;
import org.dynamo.database.reverse.emf.configuration.configuration.Configuration;
import org.dynamo.database.reverse.emf.configuration.configuration.Mapping;
import org.dynamo.database.reverse.emf.configuration.configuration.ProfileType;
import org.dynamo.database.reverse.emf.configuration.configuration.Profiles;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.uml2.uml.AggregationKind;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.PrimitiveType;
import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.UMLFactory;

public class UMLUtilities {

	private static final Log log = Log.getLog(UMLUtilities.class);
	
	private Map<String, PrimitiveType> stdTypes;
	private Model model;
	private Profile umlStdProfile;
	private Profile umlDatabase;
	
	private Map<String,Mapping> typeMapping; 
	private UMLProfiles umlProfiles;

	public UMLUtilities(IFile configfile) {
		umlProfiles = new UMLProfiles();
		// create a model.
		model = UMLFactory.eINSTANCE.createModel();
		umlProfiles.setModel(model);
		typeMapping = new HashMap<String, Mapping>(20);
		
		configfile.getLocationURI();
		try {
			configfile.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor());
		} catch (CoreException e) {
			log.error(e.getMessage());
		}
		Configuration cfg = ConfigLoader.load(configfile);
		for(Profiles profile : cfg.getProfiles())
		{
			if(profile.getType() == ProfileType.DATATYPE)
			{
				umlProfiles.addLibrary(profile);
			} else {
				umlProfiles.addProfile(profile);
			}
		}
		for(Mapping mapping : cfg.getMappings())
		{
			typeMapping.put(mapping.getFrom().toLowerCase(), mapping);
		}
	}
	
	public Stereotype getStereotype(String stereotypeName)
	{
		return umlProfiles.getStereotype(stereotypeName);
	}
	
	public Type getType(String typeName)
	{
		if(typeMapping.containsKey(typeName.toLowerCase()))
		{
			return umlProfiles.getType(
					typeMapping.get(typeName.toLowerCase()).getTo());
		}
		return null;
	}
	
	

	public Stereotype getUmlStdStereotype(String value)
	{
		for(Element element : umlStdProfile.getOwnedElements())
		{
			if(element instanceof Stereotype)
			{
				if(((Stereotype) element).getName().equals(value))
					return (Stereotype) element;
			}
		}
		return null;
	}
	
	public Stereotype getDatabaseStereotype(String value)
	{
		for(Element element : umlDatabase.getOwnedElements())
		{
			if(element instanceof Stereotype)
			{
				if(((Stereotype) element).getName().equals(value))
					return (Stereotype) element;
			}
		}
		return null;
	}

	/**
	 * Create an UML model.
	 * 
	 * @param name
	 *            of the model
	 * 
	 * @return the created model
	 */
	public Model createUmlModel(String name) {
		model.setName(name);
		return model;
	}

	public Association createAssociation(final String name, final Classifier start,
			final Classifier end, org.eclipse.uml2.uml.Package package_) {
		package_.eSetDeliver(false);
		Association uml2Association = UMLFactory.eINSTANCE.createAssociation();
		uml2Association.setName(name);
		// the start of the association
		Property firstEnd = uml2Association.createOwnedEnd("src", start);
		// the end of the association
		Property secondEnd = uml2Association.createOwnedEnd("dest", end);
		firstEnd.setAggregation(AggregationKind.NONE_LITERAL);
		
		uml2Association.getNavigableOwnedEnds().add(secondEnd);
		//uml2Association.getNavigableOwnedEnds().add(firstEnd);

		package_.getPackagedElements().add(uml2Association);
		package_.eSetDeliver(true);

		return uml2Association;
	}
	
	public Association createAssociationMultiplicity(final String name, 
			final Classifier start, 
			int startSrc,
			int endSrc,
			final Classifier end, 
			int startEnd,
			int endEnd,
			org.eclipse.uml2.uml.Package package_) {
		package_.eSetDeliver(false);
		Association uml2Association = UMLFactory.eINSTANCE.createAssociation();
		uml2Association.setName(name);
		
		// the start of the association
		Property firstEnd = uml2Association.createOwnedEnd("src", start);
		firstEnd.setLower(endSrc);
		firstEnd.setUpper(startSrc);
		
		// the end of the association
		Property secondEnd = uml2Association.createOwnedEnd("dest", end);
		secondEnd.setUpper(startEnd);
		secondEnd.setLower(endEnd);
		
		firstEnd.setAggregation(AggregationKind.NONE_LITERAL);
		
		uml2Association.getNavigableOwnedEnds().add(secondEnd);

		package_.getPackagedElements().add(uml2Association);
		package_.eSetDeliver(true);

		return uml2Association;
	}

	public Package createPackage(final Model model, String packageName) {
		Package package_ = model.createNestedPackage(packageName);
		return package_;
	}

	public Package createRecurrentPackage(final String semicolPackageName,
			Model model) {
		String[] packageNames = semicolPackageName.split("::");
		Package package_ = null;
		if (packageNames.length > 0) {
			package_ = createPackage(model, packageNames[0]);
			for (int i = 1; i < packageNames.length; i++) {
				package_ = package_.createNestedPackage(packageNames[i]);
			}
		}
		return package_;
	}

	public Class createClass(final String name, Package package_) {
		Class class_ = package_.createOwnedClass(name, false);
		return class_;
	}

	public Property createSimpleProperty(final String name, final Type type,
			final Class class_) {
		Property property = class_.createOwnedAttribute(name, type);
		return property;
	}

	public Type getUMLType(final String name) {
		log.debug("Type: "+name);
		if(name != null)
			if(stdTypes.containsKey(name.toLowerCase()))
				return stdTypes.get(name.toLowerCase());
		return null;
	}

	/**
	 * Check if the StereotypedElement has the given stereotype.
	 * 
	 * @param stereotypeName
	 *            The name of the stareotype to find.
	 * 
	 * @return true if the stereotype is found.
	 */
	public boolean hasStereotype(String stereotypeName, Element element) {
		boolean has = false;

		if (element != null) {
			Iterator<Stereotype> i = element.getAppliedStereotypes().iterator();
			Stereotype currentStereotype;
			while (i.hasNext() && !has) {
				currentStereotype = (Stereotype) i.next();
				if (currentStereotype.getName().equals(stereotypeName)) {
					has = true;
				}
			}
		}
		return has;
	}

	public void save(String modelPath, Model umlModel) {
		UMLResources.save(modelPath, umlModel);		
	}
}